﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Domain.ActiveDirectory.Model;
using Domain.Context;
using Domain.Logging;
using Domain.Provider;
using WebUI.DataModel;

namespace WebUI.Controllers
{
    public class NMCTerritoryController : NMCToolControllerBase
    {
        private IDataProvider<NMCTerritory> _nmcTerritoryRepository;
        private IDataProvider<AccountType> _accountTypeRepository;
        private IDataProvider<User> _nmcRepository;

        public NMCTerritoryController(
            LogService logger,
            IDataProvider<NMCTerritory> nmcTerritoryRepository,
            IDataProvider<AccountType> accountTypeRepository,
            IDataProvider<User> nmcRepository) : base(logger)
        {
            _nmcTerritoryRepository = nmcTerritoryRepository;
            _accountTypeRepository = accountTypeRepository;
            _nmcRepository = nmcRepository;
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            ViewBag.NMCs =
                from nmc in _nmcRepository.Entities
                select nmc.CompleteName;

            ViewBag.AccountTypes =
                from accountType in _accountTypeRepository.Entities
                where accountType.Active
                select accountType.NMC_AccountTypeName;
            
        }

        public ActionResult NMCTerritoryGrid()
        {
            try
            {
                return PartialView("_NMCTerritoryGrid", GetJoinedModels());
            }
            catch (Exception e)
            {
                LogException(this, e);
                return RedirectToAction("error", "DatabaseError");
            }
        }

        public ActionResult Index()
        {
            try
            {
                return View(GetJoinedModels());
            }
            catch (Exception e)
            {
                LogException(this, e);
                return RedirectToAction("error", "DatabaseError");
            }
        }

        public ActionResult Save(NMCTerritoryModel territoryModel)
        {
            try
            {
                var nmcTerritoryToPersist = territoryModel.Territory;

                IEnumerable<AccountType> matchingAccountTypes =
                    (from accountType in _accountTypeRepository.Entities
                     where accountType.NMC_AccountTypeName == territoryModel.AccountTypeName
                     select accountType);

                int errorCount = 0;
                bool validTerritoryData = true;
                string validAccount = null;
                AccountType matchingAccountType;
                StringBuilder errorText = new StringBuilder();


                if (matchingAccountTypes.Count() <= 0)
                {
                    validTerritoryData = false;
                    errorCount++;
                    errorText.AppendLine(String.Format("{0}. Please select a new account type for this Territory.", errorCount));
                }
                else
                {
                    matchingAccountType = matchingAccountTypes.First();
                    nmcTerritoryToPersist.NMC_AccountType = matchingAccountType.NMC_AccountTypeID;

                    if (!matchingAccountType.Active)
                    {
                        validTerritoryData = false;
                        errorCount++;
                        errorText.AppendLine(String.Format("{0}. The {1} territory is using an inactive account type: {2}.", errorCount, territoryModel.TerritoryName, matchingAccountType.NMC_AccountTypeName));
                    }
                    else
                        LogService.WriteWarning("User {0} is populating the territory {1} with an invalid account type.", User.Identity.Name, matchingAccountType.NMC_AccountTypeName);
                }


                if (!ValidateGLAccount(nmcTerritoryToPersist.NMC_GL_Acct, out validAccount))
                {
                    validTerritoryData = false;
                    errorCount++;
                    errorText.AppendLine(String.Format("{0}. The GL account for this row is invalid: {1}.", errorCount, nmcTerritoryToPersist.NMC_GL_Acct));
                }
                else
                    LogService.WriteWarning("User {0} entered invalid GL Account {1}.", User.Identity.Name, nmcTerritoryToPersist.NMC_GL_Acct);

                if (validTerritoryData)
                {
                    nmcTerritoryToPersist.NMC_GL_Acct = validAccount;
                    User nmc = FindNMCByName(_nmcRepository.Entities, territoryModel.NMC.CompleteName);

                    if (nmc != null && nmc.IsActive)
                        nmcTerritoryToPersist.NMC_PersonAcct = nmc.Email;
                    else
                        nmcTerritoryToPersist.NMC_PersonAcct = "";

                    _nmcTerritoryRepository.SaveEntity(territoryModel.Territory, User);
                }



                if (errorText.Length > 0)
                    ViewBag.Error = errorText.ToString();

                return PartialView("_NMCTerritoryGrid", GetJoinedModels());
            }
            catch (Exception e)
            {
                LogException(this, e);
                return RedirectToAction("error", "DatabaseError");
            }
        }

        public ActionResult Delete(NMCTerritoryModel territoryModel)
        {
            try
            {
                _nmcTerritoryRepository.DeleteEntity(territoryModel.Territory, User);
                return PartialView("_NMCTerritoryGrid", GetJoinedModels());
            }
            catch (Exception e)
            {
                LogException(this, e);
                return RedirectToAction("error", "DatabaseError");
            }
        }

        private IEnumerable<NMCTerritoryModel> GetJoinedModels()
        {
            var territory = _nmcTerritoryRepository.Entities;
            var accounts = _accountTypeRepository.Entities;
            var nmcs = _nmcRepository.Entities;

            IEnumerable<NMCTerritoryModel> models = territory.Join(accounts,
                key => key.NMC_AccountType, accountType => accountType.NMC_AccountTypeID ,
                (outerKey, innerKey) => new NMCTerritoryModel()
                {
                    NMC = FindNMCByEmail(nmcs, outerKey.NMC_PersonAcct),
                    Territory = outerKey,
                    AccountType = innerKey
                });

            return models;
        }

        // Finds an NMC in the specified enumerable matching the specified NMC name
        private User FindNMCByName(IEnumerable<User> allNMCs, string nmcName)
        {
            User match = null;
            var nmcs = allNMCs.Where(nmc => nmc.CompleteName == nmcName);
            if (nmcs.Count() > 0)
                match = nmcs.First();
            return match;
        }

        // Finds an NMC in the specified enumerable matching the specified NMC name
        private User FindNMCByEmail(IEnumerable<User> allNMCs, string email)
        {
            User match = null;
            var nmcs = allNMCs.Where(nmc => nmc.Email == email);
            if (nmcs.Count() > 0)
                match = nmcs.First();
            return match;
        }


        private bool ValidateGLAccount(string text, out string fixedText)
        {
            bool valid = false;
            fixedText = null;

            text = text.Trim();

            string[] glAccountSegments = text.Split('-');

            if(glAccountSegments.Length == 2)
            {
                string firstPart = glAccountSegments[0];
                string secondPart = glAccountSegments[1];
                int parsedFirstPiece = 0;
                int parsedSecondPiece = 0;

                valid = 
                    (firstPart.Length == 4 && secondPart.Length == 3 &&
                    int.TryParse(firstPart, out parsedFirstPiece) &&
                    int.TryParse(secondPart, out parsedSecondPiece));

                if (valid)
                    fixedText = String.Format("{0}-{1}", firstPart.Trim(), secondPart.Trim());
            }
           

            return valid;
        }
    }
}
